package com.lap.auth.application.authentication;

import com.lap.auth.application.authentication.assembler.AuthAssembler;
import com.lap.auth.application.authentication.command.LoginCmd;
import com.lap.auth.application.authentication.command.RefreshCmd;
import com.lap.auth.application.authentication.dto.RefreshTokenDto;
import com.lap.auth.application.authentication.dto.TokenDto;
import com.lap.auth.application.authentication.dto.UserDto;
import com.lap.auth.domain.menu.repository.MenuRepository;
import com.lap.auth.domain.user.User;
import com.lap.auth.domain.user.repository.UserRepository;
import com.lap.auth.domain.user.repository.UserRoleRepository;
import com.lap.auth.shared.errors.UserError;
import com.lap.auth.shared.ports.JwtService;
import com.lap.framework.common.exception.BizException;
import java.util.Date;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

@RequiredArgsConstructor
@Service
public class AuthQueryService {

  private final AuthAssembler authAssembler;
  private final JwtService jwtService;
  private final UserRepository userRepository;
  private final UserRoleRepository userRoleRepository;
  private final MenuRepository menuRepository;
  private final PasswordEncoder passwordEncoder;

  public TokenDto login(LoginCmd cmd) {
    User user =
        userRepository
            .findByUserName(cmd.getUserName())
            .orElseThrow(() -> BizException.newBiz(UserError.LOGIN_FAIL));
    user.assertUserActive();

    if (!passwordEncoder.matches(cmd.getPassword(), user.getPassword())) {
      throw BizException.newBiz(UserError.LOGIN_FAIL);
    }
    return resolveToken(user);
  }

  /**
   * 生成Token
   *
   * @param user 用户信息
   * @return token
   */
  private TokenDto resolveToken(User user) {
    List<Integer> roleIds = userRoleRepository.findByUserId(user.getId());
    if (roleIds.isEmpty()) {
      throw BizException.newBiz(UserError.NO_AUTH);
    }

    List<String> roles = roleIds.stream().map(String::valueOf).toList();
    List<String> permissions = menuRepository.findOptionsByRoleIds(roleIds);

    String accessToken = jwtService.accessToken(user.getId());
    String refreshToken = jwtService.refreshToken(user.getId());

    // TODO 保存刷新 TOKEN

    return TokenDto.builder()
        .userId(user.getId())
        .userName(user.getUserName())
        .fullName(user.getFullName())
        .deptId(user.getOrgId())
        .accessToken(accessToken)
        .refreshToken(refreshToken)
        .expires(jwtService.expiration())
        .roles(roles)
        .permissions(permissions)
        .build();
  }

  public RefreshTokenDto refreshToken(RefreshCmd cmd) {
    Integer userId = jwtService.validateJwt(cmd.getRefreshToken());
    // TODO 验证刷新 TOKEN
    String token = jwtService.accessToken(userId);
    Date expiration = jwtService.expiration();
    return new RefreshTokenDto(token, cmd.getRefreshToken(), expiration);
  }

  public UserDto validateToken(String token) {
    Integer userId = jwtService.validateJwt(token);
    User user =
        userRepository
            .findById(userId)
            .orElseThrow(() -> BizException.newBiz(UserError.USER_NOT_FOUND));
    return authAssembler.toDto(user);
  }
}
